
// This code is provided without guarantee of any kind explicit or implied.
// You may freely use or modify this code for any purpose.
//
// Written by Bill Long. - Feb 2008
// william.long@ripnet.com
//
// I request that if you use it, you mention my name somewhere in the credits

using System;
using System.Collections.Generic;
using System.Text;
using mshtml;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;

// Namespace used should allow you to just plug into your demo application
namespace DemoApp
{
    #region HtmlElementEventArgs
    public delegate void HtmlElementEvent(object sender, HtmlElementEventArgs e);
    /// <summary>
    /// The event object passed to the OnHtmlElementEvent handler.
    /// </summary>
    public class HtmlElementEventArgs : System.EventArgs
    {
        /// <summary>
        /// The event object passed to the OnHtmlElementEvent handler.
        /// </summary>
        /// <param name="pElem">The element that caused the event as a mshtml.IHTMLElement</param>
        /// <param name="pEventType">A string representing the type of event that occured. (onClick, onChange, etc)</param>
        /// <param name="pBubbled">A boolean. If true there was a handler assigned to this event other than this one and 
        /// was executed before processing this handler. Otherwise, there was no handler 
        /// other than this one assigned to this event</param>
        public HtmlElementEventArgs(IHTMLElement pElem, string pEventType, Boolean pBubbled)
        {
            srcElement = pElem;
            owningDocument = (IHTMLDocument)pElem.document;
            eventType = pEventType;
            bubbled = pBubbled;
        }
        /// <summary>
        /// The element that triggered the event
        /// </summary>
        public IHTMLElement srcElement;
        /// <summary>
        /// The document that contains the element that triggered the event
        /// </summary>
        public IHTMLDocument owningDocument;
        /// <summary>
        /// The type of event that occured
        /// </summary>
        public string eventType;
        /// <summary>
        /// If true there was a handler assigned to this event other than this one and 
        /// was executed before processing this handler. Otherwise, there was no handler 
        /// other than this one assigned to this event
        /// </summary>
        public Boolean bubbled;
    }
    #endregion

    #region HtmlEventHandler
    /// <summary>
    /// This is the main class for the html element event handler. To use it you simply
    /// create an instance and set the function you want to call when an event occurs.
    /// </summary>
    public class HtmlEventHandler
    {
        /// <summary>
        /// Fires when event occurs on one of the HTML elements. 
        /// </summary>
        public event HtmlElementEvent OnHtmlElementEvent = null;
        /// <summary>
        /// This is the constructor for the HtmlEventHandler class.
        /// </summary>
        /// <param name="pDocument">The mshtml.IHTMLDocument2 you want to handle element events on</param>
        public HtmlEventHandler(IHTMLDocument2 pDocument)
        {
            // What I am doing here should be pretty straight forward. I loop through all of the
            // elements in the document and set the handler for the events I want to handle. Not all 
            // events are fired by all elements so I only add the events that are relevant to the
            // element type.

            // You can set events for any Html element, not just the ones I'm handling here.

            // What is important to note is that when you set the event as I do below. you replace
            // any event that was already assigned. To handle this problem, you pass the current
            // handler (value will be a COM Object type or System.DBNull) as the third parameter of 
            // the OnHtmlElementEvent constructor so it can be called by our handler when an event occurs.

            // See the comments on the OnHtmlElementEvent class for more informantion on how to create
            // a handler than can be called this way.
            foreach (IHTMLElement2 _elem in (IHTMLElementCollection)pDocument.all)
            {
                if (_elem is mshtml.IHTMLInputElement)
                {
                    mshtml.HTMLInputElementClass elem = _elem as mshtml.HTMLInputElementClass;
                    elem.onfocus = new OnHtmlElementEvent(this, elem, elem.onfocus, "onfocus");
                    elem.onblur = new OnHtmlElementEvent(this, elem, elem.onblur, "onblur");
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                    elem.onchange = new OnHtmlElementEvent(this, elem, elem.onchange, "onchange");
                }
                else if (_elem is mshtml.IHTMLSelectElement)
                {
                    mshtml.HTMLSelectElementClass elem = _elem as mshtml.HTMLSelectElementClass;
                    elem.onfocus = new OnHtmlElementEvent(this, elem, elem.onfocus, "onfocus");
                    elem.onblur = new OnHtmlElementEvent(this, elem, elem.onblur, "onblur");
                    elem.onchange = new OnHtmlElementEvent(this, elem, elem.onchange, "onchange");
                }
                else if (_elem is mshtml.IHTMLImgElement)
                {
                    mshtml.HTMLImgClass elem = _elem as mshtml.HTMLImgClass;
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLAnchorElement)
                {
                    mshtml.HTMLAnchorElementClass elem = _elem as mshtml.HTMLAnchorElementClass;
                    elem.onfocus = new OnHtmlElementEvent(this, elem, elem.onfocus, "onfocus");
                    elem.onblur = new OnHtmlElementEvent(this, elem, elem.onblur, "onblur");
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLButtonElement)
                {
                    mshtml.HTMLButtonElementClass elem = _elem as mshtml.HTMLButtonElementClass;
                    elem.onfocus = new OnHtmlElementEvent(this, elem, elem.onfocus, "onfocus");
                    elem.onblur = new OnHtmlElementEvent(this, elem, elem.onblur, "onblur");
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLDivElement)
                {
                    mshtml.HTMLDivElementClass elem = _elem as mshtml.HTMLDivElementClass;
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLSpanElement)
                {
                    mshtml.HTMLSpanElementClass elem = _elem as mshtml.HTMLSpanElementClass;
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLTableRow)
                {
                    mshtml.HTMLTableRowClass elem = _elem as mshtml.HTMLTableRowClass;
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLTable)
                {
                    mshtml.HTMLTableClass elem = _elem as mshtml.HTMLTableClass;
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLTableCell)
                {
                    mshtml.HTMLTableCellClass elem = _elem as mshtml.HTMLTableCellClass;
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else if (_elem is mshtml.IHTMLLabelElement)
                {
                    mshtml.HTMLLabelElementClass elem = _elem as mshtml.HTMLLabelElementClass;
                    elem.onfocus = new OnHtmlElementEvent(this, elem, elem.onfocus, "onfocus");
                    elem.onblur = new OnHtmlElementEvent(this, elem, elem.onblur, "onblur");
                    elem.onclick = new OnHtmlElementEvent(this, elem, elem.onclick, "onclick");
                    elem.ondblclick = new OnHtmlElementEvent(this, elem, elem.ondblclick, "ondblclick");
                    elem.onmousedown = new OnHtmlElementEvent(this, elem, elem.onmousedown, "onmousedown");
                    elem.onmouseup = new OnHtmlElementEvent(this, elem, elem.onmouseup, "onmouseup");
                    elem.onmouseover = new OnHtmlElementEvent(this, elem, elem.onmouseover, "onmouseover");
                    elem.onmousemove = new OnHtmlElementEvent(this, elem, elem.onmousemove, "onmousemove");
                    elem.onmouseout = new OnHtmlElementEvent(this, elem, elem.onmouseout, "onmouseout");
                    elem.onkeypress = new OnHtmlElementEvent(this, elem, elem.onkeypress, "onkeypress");
                    elem.onkeydown = new OnHtmlElementEvent(this, elem, elem.onkeydown, "onkeydown");
                    elem.onkeyup = new OnHtmlElementEvent(this, elem, elem.onkeyup, "onkeyup");
                }
                else
                { /* other element types */}
            }
        }
        /// <summary>
        /// Called by the handler object to fire the HtmlEventHandler.OnHtmlElementEvent event so
        /// the method assigned by the developer gets called.
        /// </summary>
        /// <param name="pArg">An HtmlElementEventArgs object containing the details of the event that occured</param>
        internal void FireHtmlElementEvent(HtmlElementEventArgs pArg)
        { OnHtmlElementEvent(this, pArg); }
    }
    #endregion

    #region OnHtmlElementEvent
    /// <summary>
    /// This is the COM interface that gets called when an event occurs.
    /// I set the interface type to AutoDual so it will expose a IDispatch
    /// and an IUnknown interface. I only really need the IDispatch interface.
    /// When an element fires an event it looks for the default parameter-less method
    /// on the COM object assigned to the event and executes it. The key to making this work
    /// is defining the default method. See below for more info
    /// </summary>
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class OnHtmlElementEvent
    {
        #region Variables
        private mshtml.IHTMLElement mElem; // store the element we are handling the event on
        private object mOldHandler; // store any existing handler that was replace by this one
        private string mEventType; // store the type of event that occured as a string
        private HtmlEventHandler mHandler; // store the HtmlEventHandler that set this event so
        // we can fire the OnHtmlElementEvent
        #endregion

        /// <summary>
        /// Constructor fo object the OnHtmlElementEvent
        /// </summary>
        /// <param name="pHtmlEventHandler"></param>
        /// <param name="pElem">The mshtml.IHTMLElement we are handling the event on</param>
        /// <param name="pOldHandler">When you set an event, you replace any event that was already assigned. 
        /// You pass the current handler (value will be a COM Object type or System.DBNull) as the third 
        /// parameter of the OnHtmlElementEvent constructor so it can be called when an event occurs.</param>
        /// <param name="pEventType">The type of event that occured as a string. (onClick, onChange, etc.</param>
        public OnHtmlElementEvent(HtmlEventHandler pHtmlEventHandler, IHTMLElement2 pElem, object pOldHandler, string pEventType)
        {
            mElem = pElem as mshtml.IHTMLElement;
            mOldHandler = pOldHandler;
            mEventType = pEventType;
            mHandler = pHtmlEventHandler;
        }
        /// <summary>
        /// The method that gets called when an event occurs.
        /// The key thing to note here is the "[DispId(0)]" attribute.
        /// Setting this attribute on a method makes it the default.
        /// </summary>
        [DispId(0)]
        public void HtmlElementEvent()
        {
            bool bubbled = false;
            // Call the old event handler first, but only if there was one. If the was no previous
            // handler the value of "mOldHandler" will be System.DBNull.Value
            if (mOldHandler != null && mOldHandler != System.DBNull.Value)
            {
                // Get a reference to the IDisplatch type
                Type lIDispatch = Type.GetTypeFromCLSID(new System.Guid("{00020400-0000-0000-C000-000000000046}"));
                // Invoke the default method "[DispID=0]"
                lIDispatch.InvokeMember("[DispID=0]", System.Reflection.BindingFlags.InvokeMethod, null, mOldHandler, null);
                bubbled = true; // Set this to true because an existing event was called
            }
            // Call the event handler in the parent HtmlEventHandler class to notify the calling
            // application.
            mHandler.FireHtmlElementEvent(new HtmlElementEventArgs(mElem, mEventType, bubbled));
        }
    }
    #endregion
}

/*
 //Sample Usage
private void cEXWB1_DocumentComplete(object sender, csExWB.DocumentCompleteEventArgs e)
{
    if (e.istoplevel)

    {
        HtmlEventHandler handler = new HtmlEventHandler((mshtml.IHTMLDocument2)cEXWB1.GetActiveDocument());
        handler.OnHtmlElementEvent+=new HtmlElementEvent(handler_OnHtmlElementEvent);
    }
}

void handler_OnHtmlElementEvent(object sender, HtmlElementEventArgs e)
{
    if ((e.srcElement is mshtml.IHTMLInputElement) && (e.eventType.ToLower()=="onclick"))
    {
        mshtml.HTMLInputElementClass elem = e.srcElement as mshtml.HTMLInputElementClass;
        if (e.bubbled)
            MessageBox.Show("You clicked an input element id=" + elem.id + ", name=" + elem.name + ", type=" + elem.type +
                ". There was already an event assigned to this element and it was executed.");
        else
            MessageBox.Show("You clicked an input element id=" + elem.id + ", name=" + elem.name + ", type=" + elem.type +
                ". There was no event already assigned to this element.");
    }
    else if ((e.srcElement is mshtml.IHTMLInputElement) && (e.eventType.ToLower()=="onchange"))
    {
        mshtml.HTMLInputElementClass elem = e.srcElement as mshtml.HTMLInputElementClass;
        if (e.bubbled)
            MessageBox.Show("You changed text for an input element id=" + elem.id + ", name=" + elem.name + ", type=" + elem.type +
                ". There was already an event assigned to this element and it was executed.");
        else
             MessageBox.Show("You changed text for an input element id=" + elem.id + ", name=" + elem.name + ", type=" + elem.type +
                ". There was no event already assigned to this element.");
    }
    else if ((e.srcElement is mshtml.IHTMLImgElement) && (e.eventType.ToLower()=="onclick"))
    {
        mshtml.HTMLImgClass elem = e.srcElement as mshtml.HTMLImgClass;
        if (e.bubbled)
            MessageBox.Show("You clicked an image element id=" + elem.id + ", name=" + elem.name + 
                ". There was already an event assigned to this element and it was executed.");
        else
            MessageBox.Show("You clicked an image element id=" + elem.id + ", name=" + elem.name + 
                ". There was no event already assigned to this element.");
    }
    else if ((e.srcElement is mshtml.IHTMLAnchorElement) && (e.eventType.ToLower()=="onclick"))
    {
        mshtml.HTMLAnchorElementClass elem = e.srcElement as mshtml.HTMLAnchorElementClass;
        if (e.bubbled)
            MessageBox.Show("You clicked an anchor element id=" + elem.id + ", name=" + elem.name +  
                ". There was already an event assigned to this element and it was executed.");
        else
            MessageBox.Show("You clicked an anchor element id=" + elem.id + ", name=" + elem.name + 
                ". There was no event already assigned to this element.");

    }
}
 
 */